import pythoncom
import win32clipboard
import pywintypes
from win32com.server.util import NewEnum, wrap
from win32com.server.exception import COMException
import win32con

class DataFormats(object):
    _codes_ = {
        1: "CF_TEXT",
        7: "CF_OEMTEXT",
        13: "CF_UNICODETEXT",
        16: "CF_LOCALE"}
    _names_ = dict([(i[1], i[0]) for i in _codes_.items()])
        
    def GetFormatName(self, code):
        if code >= 0xc000:
            return win32clipboard.GetClipboardFormatName(code)
        if code in self._codes_:
            return self._codes_[code]
        return "CF_UNKNOWN_" + str(code)
        
    def GetFormat(self, name):
        if name in self._names_:
            return self._names_[name]
        if name.startswith("CF_UNKNOWN_"):
            return int(name[6:])
        return win32clipboard.RegisterClipboardFormat(name)
DataFormats = DataFormats()

IDataObject_Methods = """GetData GetDataHere QueryGetData
                         GetCanonicalFormatEtc SetData EnumFormatEtc
                         DAdvise DUnadvise EnumDAdvise""".split()
    
def WrapCOMObject(ob, iid=None):
    return wrap(ob, iid=iid, useDispatcher = 0)

class OleDataObject:
    _com_interfaces_ = [pythoncom.IID_IDataObject]
    _public_methods_ = IDataObject_Methods
    
    def __init__(self, format, data):
        self.data = data
        format = DataFormats.GetFormat(format)
        self.format = format, None, pythoncom.DVASPECT_CONTENT, -1, pythoncom.TYMED_HGLOBAL
        self.supported_fe = [self.format]
    
    def _query_interface_(self, iid):
        if iid==pythoncom.IID_IEnumFORMATETC:
            return NewEnum(self.supported_fe, iid=iid)

    def GetData(self, fe):
        ret_stg = None
        cf, target, aspect, index, tymed  = fe
        if aspect & pythoncom.DVASPECT_CONTENT and tymed==pythoncom.TYMED_HGLOBAL:
            if cf == self.format[0]:
                ret_stg = pythoncom.STGMEDIUM()
                # ensure always 'bytes' by encoding string.
                ret_stg.set(pythoncom.TYMED_HGLOBAL, self.data)

        if ret_stg is None:
            raise COMException(hresult=winerror.E_NOTIMPL)
        return ret_stg

    def GetDataHere(self, fe):
        raise COMException(hresult=winerror.E_NOTIMPL)

    def QueryGetData(self, fe):
        cf, target, aspect, index, tymed  = fe
        if aspect & pythoncom.DVASPECT_CONTENT == 0:
            raise COMException(hresult=winerror.DV_E_DVASPECT)
        if tymed != pythoncom.TYMED_HGLOBAL:
            raise COMException(hresult=winerror.DV_E_TYMED)
        return None # should check better

    def GetCanonicalFormatEtc(self, fe):
        RaiseCOMException(winerror.DATA_S_SAMEFORMATETC)
        # return fe

    def SetData(self, fe, medium):
        raise COMException(hresult=winerror.E_NOTIMPL)

    def EnumFormatEtc(self, direction):
        if direction != pythoncom.DATADIR_GET:
            raise COMException(hresult=winerror.E_NOTIMPL)
        return NewEnum(self.supported_fe, iid=pythoncom.IID_IEnumFORMATETC)

    def DAdvise(self, fe, flags, sink):
        raise COMException(hresult=winerror.E_NOTIMPL)

    def DUnadvise(self, connection):
        raise COMException(hresult=winerror.E_NOTIMPL)

    def EnumDAdvise(self):
        raise COMException(hresult=winerror.E_NOTIMPL)

        
class DataObject(object):
    def __init__(self, o):
        self.o = o
        self.formats = {}
        for format in self.o.EnumFormatEtc(pythoncom.DATADIR_GET):
            self.formats[DataFormats.GetFormatName(format[0])] = format
        
    def GetFormats(self):
        return self.formats.keys()
    
    def GetData(self, format):
        if format not in self.formats:
            return None
        
        format = self.formats[format]
        if format[4] & pythoncom.TYMED_HGLOBAL:
            format = format[:-1] + (pythoncom.TYMED_HGLOBAL,)
            #format = format[:-1] + (pythoncom.TYMED_ISTREAM,)
            stg = self.o.GetData(format)
            return stg.data
        else:
            raise Exception("no TYMED_HGLOBAL")
    
pythoncom.OleInitialize()

def GetDataObject():
    o = pythoncom.OleGetClipboard()
    return DataObject(o)
    
def SetDataObject(format, data):
    do = OleDataObject(format, data)
    do = WrapCOMObject(do, iid=pythoncom.IID_IDataObject)
    pythoncom.OleSetClipboard(do)
    do = None # clear my ref!
    pythoncom.OleFlushClipboard()

if __name__ == "__main__":
    o = GetDataObject()
    print o.GetFormats()
    print repr(o.GetData('CF_UNICODETEXT'))
    print repr(o.GetData('CF_Gppw_Cir_Mid_Cir'))
    print SetDataObject("CF_TEXT", "Wolf System")
    f = open("test.dat", "rb")
    data = f.read()
    print "start"
    print repr(data)
    f.close()
    print SetDataObject("CF_Gppw_Cir_Mid_Cir", data)
    print "end"

